﻿using System;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;

using alib;
using alib.BitArray;
using alib.Enumerable;
using alib.Debugging;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// bit-vector of appropriate features for a type, a wrapper for a BitArr object that caches the index positions 
	/// of its 1s-bits.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed class FeatureConfig : IIdentList<FeatureInfo>
	{
		public unsafe FeatureConfig(FeatMgr ftm, BitArr ba, int fc_ix)
		{
			this.ftm = ftm;
			this.fc_ix = fc_ix;
			this.ba = ba;
			this.rg_fix = ba.OnesPositions();
#if AUTOTUNE
			this.rg_fix_autotune = rg_fix;
#endif
			if ((this.modulus = rg_fix.Length) > 1)
			{
				Array.Sort(rg_fix, 0, modulus, ftm.FeatureDisplayOrderComparer);
				int max = int.MinValue;
				int min = int.MaxValue;
				foreach (int fix in rg_fix)
				{
					if (fix < min)
						min = fix;
					if (fix > max)
						max = fix;
				}
				int range = max - min + 1, gen = 0;
				int* test_arr = stackalloc int[range];
				while (true)
				{
					gen++;
					foreach (int fix in rg_fix)
					{
						int slot = fix % modulus;
						if (test_arr[slot] == gen)
							goto collision;
						test_arr[slot] = gen;
					}
					break;
				collision:
					if (++modulus > range)
						throw new Exception();
				}
			}
		}


		public readonly BitArr ba;

		public readonly int[] rg_fix;
#if AUTOTUNE
		public int[] rg_fix_autotune;
#endif
		public readonly int modulus;

		public int c_refs;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		readonly FeatMgr ftm;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly int fc_ix;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public FeatureConfig[] _dbg_child_configs { get { return ChildConfigs.ToArray(); } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IEnumerable<FeatureConfig> ChildConfigs
		{
			get { return ftm.rg_fc.Where(fc => fc != this && ba.IsSubsumedByFull(fc.ba)); }
		}

		public FeatureInfo this[int index] { get { return ftm[rg_fix[index]]; } }

		public int Count { get { return rg_fix.Length; } }

		public IEnumerator<FeatureInfo> GetEnumerator()
		{
			for (int i = 0; i < rg_fix.Length; i++)
				yield return ftm[rg_fix[i]];
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Type MaximalType
		{
			get
			{
				if (rg_fix.Length == 0 || this == ftm.Empty || this == ftm.EmptyWithFeaturesBelow)
					throw new Exception();

				return ftm.im.GetMaximalTypeForFeatures(rg_fix);
			}
		}

		ArrayTfs _root_coverage;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public ArrayTfs RootCoverageTfs
		{
			get
			{
				if (rg_fix.Length == 0 || this == ftm.Empty || this == ftm.EmptyWithFeaturesBelow)
					throw new Exception();

				if (_root_coverage == null)
				{
					_root_coverage = new ArrayTfs(
												ftm,
												ftm,
												ftm.r_none,
												Tfs.Flags.RootCoverage,
												ftm.tu.CreateEdge(MaximalType, 1, false),
												default(ICollection<arr_tfs_entry>),
												0);
				}
				return _root_coverage;
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int[] ModOrder
		{
			get
			{
				int[] arr = new int[modulus];
				for (int i = 0; i < modulus; i++)
					arr[i] = -1;
				foreach (int fix in rg_fix)
					arr[fix % modulus] = fix;
				return arr;
			}
		}

#if false
		public FeatureConfig[] _dbg_child_configs2 { get { return ChildConfigs2.ToArray(); } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IEnumerable<FeatureConfig> ChildConfigs2
		{
			get
			{
				return UsedBy.UnionMany(t => t.AllDescendants.Select(x=>x.fc));
						
					//fc.ChildConfigs2.Where(y => y != this));
				//return fcm.rg_fc.Where(fc => fc != this && ba.IsSubsumedByFull(fc.ba));
				//yield break;
			}
		}

		public Type[] _dbg_used_by { get { return UsedBy.ToArray(); } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IEnumerable<Type> UsedBy
		{
			get
			{
				Type t = t_next;
				while (t != null)
				{
					yield return t;
					t = t.t_fc_next;
				}
			}
		}
#endif
#if DEBUG
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// diagnostics
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public String Info
		{
			get
			{
				if (modulus == 0)
					return "no features";
				int[] tmp = new int[modulus];
				for (int i = 0; i < rg_fix.Length; i++)
					tmp[i] = -1;
				for (int i = 0; i < rg_fix.Length; i++)
					tmp[rg_fix[i] % modulus] = rg_fix[i];

				return tmp
					.Select((i, ix) => String.Format("{0}{1}", ix, i == -1 ? "" : "=" + i.ToString() + "/" + ftm[i].feature.ToUpper()))
					.StringJoin("  ");
			}
		}

		public override String ToString()
		{
			return String.Format("#{0,-3} feat: {1,-2} mod: {2,-2} refs: {3,-3} cc: {4,-2} [{5}]",
					fc_ix,
					rg_fix.Length,
					modulus,
					c_refs,
					ChildConfigs._Count(),
				//ChildConfigs2.Count(),
				//rg_fix.Select(i => fcm.feat_arr[i].feature.ToUpper()).StringJoin(" ")
					ModOrder.Select(i => i == -1 ? "__" : ftm[i].feature.ToUpper()).StringJoin(" ")
					);
		}
#endif

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		IIdentity IIdentity.Trace { get { return ftm; } }

		void ICollection.CopyTo(Array array, int index) { throw not.impl; }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		bool ICollection.IsSynchronized { get { return false; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		Object ICollection.SyncRoot { get { return this; } }
	};
}
